﻿using System;
using NUnit.Framework;

namespace CodeContrib.Tests
{
	public class DateTimeExtensionsTests
	{
		[Test]
		[ExpectedException(typeof (ArgumentOutOfRangeException))]
		public void ConvertFromUnixTimestamp_With_MaxValue()
		{
			// Arrange
			const double timestamp = 253402300800;

			// Act
			timestamp.FromUnixTimestamp();
		}

		[Test]
		public void ConvertFromUnixTimestamp_With_Millennium()
		{
			// Arrange
			var expected = new DateTime(2000, 1, 1, 0, 0, 0);

			const double timestamp = 946684800;

			// Act
			var actual = timestamp.FromUnixTimestamp();

			// Arrange
			Assert.AreEqual(expected, actual);
		}

		[Test]
		public void ConvertFromUnixTimestamp_With_MinimumValue()
		{
			// Arrange
			var expected = DateTime.MinValue;

			const double timestamp = -62135596800;

			// Act
			var actual = timestamp.FromUnixTimestamp();

			// Arrange
			Assert.AreEqual(expected, actual);
		}

		[Test]
		public void ConvertFromUnixTimestamp_With_Unix_Origin()
		{
			// Arrange
			var expected = new DateTime(1970, 1, 1, 0, 0, 0, 0);

			const double timestamp = 0;

			// Act
			var actual = timestamp.FromUnixTimestamp();

			// Arrange
			Assert.AreEqual(expected, actual);
		}

		[Test]
		public void ConvertToUnixTimestamp_With_MaxValue()
		{
			// Arrange
			const double expected = 253402300800;

			var dateTime = DateTime.MaxValue;

			// Act
			var actual = dateTime.ToUnixTimestamp();

			// Arrange
			Assert.AreEqual(expected, actual);
		}

		[Test]
		public void ConvertToUnixTimestamp_With_Millennium()
		{
			// Arrange
			const double expected = 946684800;

			var dateTime = new DateTime(2000, 1, 1, 0, 0, 0);

			// Act
			var actual = dateTime.ToUnixTimestamp();

			// Arrange
			Assert.AreEqual(expected, actual);
		}

		[Test]
		public void ConvertToUnixTimestamp_With_MinimumValue()
		{
			// Arrange
			const double expected = -62135596800;

			var dateTime = DateTime.MinValue;

			// Act
			var actual = dateTime.ToUnixTimestamp();

			// Arrange
			Assert.AreEqual(expected, actual);
		}

		[Test]
		public void ConvertToUnixTimestamp_With_Unix_Origin()
		{
			// Arrange
			const double expected = 0;

			var dateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0);

			// Act
			var actual = dateTime.ToUnixTimestamp();

			// Arrange
			Assert.AreEqual(expected, actual);
		}

		[Test, TestCaseSource(typeof (DateTimeSources), "Days")]
		public void ToReadableOffset_Minus_Days_ShouldReturn_DaysAgo(int days)
		{
			// Arrange
			var now = new DateTime(1970, 1, 1);

			var dateTime = now.AddDays(-days);

			var expected = string.Format("{0:0}d ago", days);

			// Act
			var actual = dateTime.ToReadableOffset(now);

			// Assert
			Assert.AreEqual(expected, actual);
		}

		[Test, TestCaseSource(typeof (DateTimeSources), "Hours")]
		public void ToReadableOffset_Minus_Hours_ShouldReturn_HoursAgo(int hours)
		{
			// Arrange
			var now = new DateTime(1970, 1, 1);

			var dateTime = now.AddHours(-hours);

			var expected = string.Format("{0:0}h ago", hours);

			// Act
			var actual = dateTime.ToReadableOffset(now);

			// Assert
			Assert.AreEqual(expected, actual);
		}

		[Test, TestCaseSource(typeof (DateTimeSources), "Minutes")]
		public void ToReadableOffset_Minus_Minutes_ShouldReturn_MinutesAgo(int minutes)
		{
			// Arrange
			var now = new DateTime(1970, 1, 1);

			var dateTime = now.AddMinutes(-minutes);

			var expected = string.Format("{0:0}m ago", minutes);

			// Act
			var actual = dateTime.ToReadableOffset(now);

			// Assert
			Assert.AreEqual(expected, actual);
		}

		[Test, TestCaseSource(typeof (DateTimeSources), "Seconds")]
		public void ToReadableOffset_Minus_Seconds_ShouldReturn_SecondsAgo(int seconds)
		{
			// Arrange
			var now = new DateTime(1970, 1, 1);

			var dateTime = now.AddSeconds(-seconds);

			var expected = string.Format("{0:0}s ago", seconds);

			// Act
			var actual = dateTime.ToReadableOffset(now);

			// Assert
			Assert.AreEqual(expected, actual);
		}

		[Test, TestCaseSource(typeof (DateTimeSources), "Days")]
		public void ToReadableOffset_Plus_Days_ShouldReturn_DaysToGo(int days)
		{
			// Arrange
			var now = new DateTime(1970, 1, 1);

			var dateTime = now.AddDays(days);

			var expected = string.Format("{0:0}d to go", days);

			// Act
			var actual = dateTime.ToReadableOffset(now);

			// Assert
			Assert.AreEqual(expected, actual);
		}

		[Test, TestCaseSource(typeof (DateTimeSources), "Hours")]
		public void ToReadableOffset_Plus_Hours_ShouldReturn_HoursToGo(int hours)
		{
			// Arrange
			var now = new DateTime(1970, 1, 1);

			var dateTime = now.AddHours(hours);

			var expected = string.Format("{0:0}h to go", hours);

			// Act
			var actual = dateTime.ToReadableOffset(now);

			// Assert
			Assert.AreEqual(expected, actual);
		}

		[Test, TestCaseSource(typeof (DateTimeSources), "Minutes")]
		public void ToReadableOffset_Plus_Minutes_ShouldReturn_MinutesToGo(int minutes)
		{
			// Arrange
			var now = new DateTime(1970, 1, 1);

			var dateTime = now.AddMinutes(minutes);

			var expected = string.Format("{0:0}m to go", minutes);

			// Act
			var actual = dateTime.ToReadableOffset(now);

			// Assert
			Assert.AreEqual(expected, actual);
		}

		[Test, TestCaseSource(typeof (DateTimeSources), "Seconds")]
		public void ToReadableOffset_Plus_Seconds_ShouldReturn_SecondsToGo(int seconds)
		{
			// Arrange
			var now = new DateTime(1970, 1, 1);

			var dateTime = now.AddSeconds(seconds);

			var expected = string.Format("{0:0}s to go", seconds);

			// Act
			var actual = dateTime.ToReadableOffset(now);

			// Assert
			Assert.AreEqual(expected, actual);
		}

		public void ToReadableOffset_ShouldReturn_JustNow()
		{
			// Arrange
			var now = new DateTime(1970, 1, 1);

			var dateTime = now;

			var expected = "just now";

			// Act
			var actual = dateTime.ToReadableOffset(now);

			// Assert
			Assert.AreEqual(expected, actual);
		}
	}
}